/**
 * 
 */
package com.lorizz.picpub.query;

import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaBuilder.In;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import com.lorizz.upm.TaskState;
import com.lorizz.upm.entity.AlbumEntity;
import com.lorizz.upm.entity.AlbumEntity_;
import com.lorizz.upm.entity.ImageEntity;
import com.lorizz.upm.entity.ImageEntity_;
import com.lorizz.upm.entity.ImageVariantEntity;
import com.lorizz.upm.entity.ImageVariantEntity_;
import com.lorizz.upm.entity.TaskEntity;
import com.lorizz.upm.entity.TaskEntity_;
import com.lorizz.upm.entity.UserProfileEntity;
import com.lorizz.upm.entity.UserProfileEntity_;
import com.lorizz.upm.repository.Repository;

/**
 * @author wind hong
 * 
 */
public class UpmQueries {

    private static UpmQueries instance;
    private Repository upmRepository;

    public void setUpmRepository(Repository upmRepository) {
        this.upmRepository = upmRepository;
    }

    public UpmQueries() {
        instance = this;
    }

    public static UpmQueries instance() {
        return instance;
    }

    private CriteriaBuilder getCriteriaBuilder() {
        return upmRepository.getCriteriaBuilder();
    }

    public CriteriaQuery<UserProfileEntity> userProfiles(String name, String password) {
        CriteriaBuilder cb = getCriteriaBuilder();
        CriteriaQuery<UserProfileEntity> query = cb.createQuery(UserProfileEntity.class);
        Root<UserProfileEntity> userProfiles = query.from(UserProfileEntity.class);
        query.where(cb.and(cb.equal(userProfiles.get(UserProfileEntity_.name), name),
                cb.equal(userProfiles.get(UserProfileEntity_.password), password)));
        return query;
    }

    public CriteriaQuery<UserProfileEntity> userProfiles(String name) {
        CriteriaBuilder cb = getCriteriaBuilder();
        CriteriaQuery<UserProfileEntity> query = cb.createQuery(UserProfileEntity.class);
        Root<UserProfileEntity> userProfiles = query.from(UserProfileEntity.class);
        query.where(cb.equal(userProfiles.get(UserProfileEntity_.name), name));
        return query;
    }

    public CriteriaQuery<AlbumEntity> albums(long userProfileId) {
        CriteriaBuilder cb = getCriteriaBuilder();
        CriteriaQuery<AlbumEntity> query = cb.createQuery(AlbumEntity.class);
        Root<AlbumEntity> albums = query.from(AlbumEntity.class);
        Path<UserProfileEntity> userProfile = albums.get(AlbumEntity_.userProfile);
        query.where(cb.equal(userProfile.get(UserProfileEntity_.id), userProfileId));
        return query;
    }

    public CriteriaQuery<Object[]> albumIdsWithImageCountsAnd1stImageId(long userProfileId) {
        CriteriaBuilder cb = getCriteriaBuilder();
        CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
        Root<AlbumEntity> albums = query.from(AlbumEntity.class);
        ListJoin<AlbumEntity, ImageEntity> join = albums.join(AlbumEntity_.images);
        query.select(cb.array(albums.get(AlbumEntity_.id), cb.count(join.get(ImageEntity_.id)), cb.min(join.get(ImageEntity_.id))));
        query.where(cb.equal(albums.get(AlbumEntity_.userProfile).get(UserProfileEntity_.id), userProfileId));
        query.groupBy(albums.get(AlbumEntity_.id));
        return query;
    }

    public CriteriaQuery<AlbumEntity> album(String title, long userProfileId) {
        CriteriaBuilder cb = getCriteriaBuilder();
        CriteriaQuery<AlbumEntity> query = cb.createQuery(AlbumEntity.class);
        Root<AlbumEntity> albums = query.from(AlbumEntity.class);
        Predicate p1 = cb.equal(albums.get(AlbumEntity_.userProfile).get(UserProfileEntity_.id), userProfileId);
        Predicate p2 = cb.equal(albums.get(AlbumEntity_.title), title);
        query.where(cb.and(p1, p2));
        return query;
    }
    
    public CriteriaQuery<ImageEntity> images(long albumId) {
        CriteriaBuilder cb = getCriteriaBuilder();
        CriteriaQuery<ImageEntity> query = cb.createQuery(ImageEntity.class);
        Root<ImageEntity> images = query.from(ImageEntity.class);
        ListJoin<ImageEntity, AlbumEntity> join = images.join(ImageEntity_.albums);
        query.where(cb.equal(join.get(AlbumEntity_.id), albumId));
        return query;
    }
    
    public CriteriaQuery<Long> imageCount(long albumId) {
        CriteriaBuilder cb = getCriteriaBuilder();
        CriteriaQuery<Long> query = cb.createQuery(Long.class);
        Root<ImageEntity> images = query.from(ImageEntity.class);
        ListJoin<ImageEntity, AlbumEntity> join = images.join(ImageEntity_.albums);
        query.select(cb.count(images.get(ImageEntity_.id)));
        query.where(cb.equal(join.get(AlbumEntity_.id), albumId));
        return query;
    }
    
    public CriteriaQuery<Long> imageCountLessThan(long albumId, long imageId) {
        CriteriaBuilder cb = getCriteriaBuilder();
        CriteriaQuery<Long> query = cb.createQuery(Long.class);
        Root<ImageEntity> images = query.from(ImageEntity.class);
        ListJoin<ImageEntity, AlbumEntity> join = images.join(ImageEntity_.albums);
        query.select(cb.count(images.get(ImageEntity_.id)));
        query.where(cb.and(cb.equal(join.get(AlbumEntity_.id), albumId), cb.lessThan(images.get(ImageEntity_.id), imageId)));
        return query;
    }
    
    public CriteriaQuery<Object[]> imageVariantsWithImageIds(List<Long> imageIds, String variantName) {
        CriteriaBuilder cb = getCriteriaBuilder();
        CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
        Root<ImageVariantEntity> imageVariants = query.from(ImageVariantEntity.class);
        query.select(cb.array(imageVariants.get(ImageVariantEntity_.image).get(ImageEntity_.id), imageVariants));
        Predicate p = cb.equal(imageVariants.get(ImageVariantEntity_.name), variantName);
        In<Long> in = cb.in(imageVariants.get(ImageVariantEntity_.image).get(ImageEntity_.id));
        for (Long id : imageIds) {
            in.value(id);
        }
        query.where(cb.and(p, in));
        return query;
    }
    
    public CriteriaQuery<ImageVariantEntity> imageVariants(List<Long> imageIds, String variantName) {
        CriteriaBuilder cb = getCriteriaBuilder();
        CriteriaQuery<ImageVariantEntity> query = cb.createQuery(ImageVariantEntity.class);
        Root<ImageVariantEntity> imageVariants = query.from(ImageVariantEntity.class);
        Predicate p = cb.equal(imageVariants.get(ImageVariantEntity_.name), variantName);
        In<Long> in = cb.in(imageVariants.get(ImageVariantEntity_.image).get(ImageEntity_.id));
        for (Long id : imageIds) {
            in.value(id);
        }
        query.where(cb.and(p, in));
        return query;
    }

    public CriteriaQuery<TaskEntity> prioritizedNewTasks() {
        CriteriaBuilder cb = getCriteriaBuilder();
        CriteriaQuery<TaskEntity> query = cb.createQuery(TaskEntity.class);
        Root<TaskEntity> tasks = query.from(TaskEntity.class);
        query.where(cb.equal(tasks.get(TaskEntity_.state), TaskState.NEW));
        query.orderBy(cb.asc(tasks.get(TaskEntity_.priority)));
        return query;
    }

    public CriteriaQuery<TaskEntity> tasks(List<Long> ids) {
        CriteriaBuilder cb = getCriteriaBuilder();
        CriteriaQuery<TaskEntity> query = cb.createQuery(TaskEntity.class);
        Root<TaskEntity> tasks = query.from(TaskEntity.class);
        In<Long> in = cb.in(tasks.get(TaskEntity_.id));
        for (Long id : ids) {
            in.value(id);
        }
        query.where(in);
        return query;
    }
    
    public CriteriaQuery<ImageEntity> images(List<Long> ids) {
    	CriteriaBuilder cb = getCriteriaBuilder();
    	CriteriaQuery<ImageEntity> query = cb.createQuery(ImageEntity.class);
    	Root<ImageEntity> images = query.from(ImageEntity.class);
    	In<Long> in = cb.in(images.get(ImageEntity_.id));
    	for (Long id : ids) {
    		in.value(id);
    	}
    	query.where(in);
    	return query;
    }

}
